package com.sikaryofficial.backend.controller;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.sikaryofficial.backend.config.EmailIgnoreWhiteProperties;
import com.sikaryofficial.backend.constant.EmailConstant;
import com.sikaryofficial.backend.constant.EmailSendSourceEnum;
import com.sikaryofficial.backend.domain.dto.req.SendMailCaptchaReq;
import com.sikaryofficial.backend.resolver.PropertiesTools;
import com.sikaryofficial.backend.service.email.EmailService;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.core.utils.HiResultUtil;
import com.sikaryofficial.common.core.utils.StringUtils;
import com.sikaryofficial.common.core.utils.file.FileTypeUtils;
import com.sikaryofficial.common.core.utils.file.MimeTypeUtils;
import com.sikaryofficial.common.core.web.controller.BaseController;
import com.sikaryofficial.common.core.web.domain.HiResult;
import com.sikaryofficial.common.log.annotation.Log;
import com.sikaryofficial.common.log.enums.BusinessType;
import com.sikaryofficial.common.redis.service.RedisService;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.RemoteFileService;
import com.sikaryofficial.system.api.RemoteUserService;
import com.sikaryofficial.system.api.domain.SysFile;
import com.sikaryofficial.system.api.domain.SysUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.sikaryofficial.backend.constant.i18n.ExceptionConstant.EMAIL_VERIFICATION_ERROR_MSG;
import static com.sikaryofficial.backend.constant.i18n.ExceptionConstant.SEND_MAIL_CAPTCHA_SUCCESS_MSG;
import static com.sikaryofficial.common.core.constant.CacheConstants.SEND_MAIL_CAPTCHA_KEY;
import static com.sikaryofficial.common.core.constant.CacheConstants.SEND_MAIL_SEND_SOURCE_KEY;

/**
 * H5页面需要新增的接口
 *
 * @author qinjinyuan
 * @date 2023-10-27
 */
@RestController
@RequestMapping("/h5InitPage")
@Api(tags = "H5页面首页相关接口")
@Slf4j
@RefreshScope
public class H5InitPageController extends BaseController {
    @Resource
    private RemoteUserService remoteUserService;
    @Resource
    private RedisService redisService;
    @Resource
    private Map<String, EmailService> emailServiceMap;
    @Autowired
    private RemoteFileService remoteFileService;
    @Value("${hismk.backend.ske.appSecret}")
    private String appSecret;
    @Value("${hismk.backend.ske.officialUrl}")
    private String skeOfficialUrl;
    @Resource
    private EmailIgnoreWhiteProperties emailIgnoreWhiteProperties;
    @Value("${hismk.backend.captchaEmailNotice.subject}")
    private String emailSubject;
    @Value("${hismk.backend.captchaEmailNotice.sendContent}")
    private String emailContent;

    @ApiOperation("ske官网授权地址")
    @GetMapping("/getCallBackUrl")
    public R<String> getCallBackUrl() {
        R<SysUser> result1 = remoteUserService.getUserByUserId(SecurityUtils.getUserId());
        if (Objects.isNull(result1) || Objects.isNull(result1.getData())) {
            return R.fail("user info is empty");
        }
        SysUser sysUser = result1.getData();
        String email = sysUser.getEmail();
        Long timestamp = System.currentTimeMillis() / 1000;
        String curSign = DigestUtil.md5Hex(email + timestamp + appSecret);
        String result = MessageFormat.format(skeOfficialUrl, email, String.valueOf(timestamp), curSign);
        return R.ok(result);
    }

    @ApiOperation("获取用户信息")
    @GetMapping("/getInfoByh5")
    public HiResult<SysUser> getInfoByh5() {
        R<SysUser> data = remoteUserService.getInfoByh5();
        if (Objects.isNull(data)) {
            return HiResultUtil.error("user info is empty");
        }
        return HiResultUtil.success(data.getData());
    }

    /**
     * 构建发送源 source key
     *
     * @param req
     */
    private String buildCurrentSendSourceKey(SendMailCaptchaReq req) {
        String sendSource = EmailSendSourceEnum.getSendSourceByCode(req.getSendSource());
        return MessageFormat.format(SEND_MAIL_SEND_SOURCE_KEY, sendSource, req.getEmailAddress());
    }

    private String buildGmailSendSourceKey(String emailAddress) {
        return MessageFormat.format(SEND_MAIL_SEND_SOURCE_KEY, EmailSendSourceEnum.GMAIL.getSendSource(), emailAddress);
    }

    private String buildNormalSendSourceKey(String emailAddress) {
        return MessageFormat.format(SEND_MAIL_SEND_SOURCE_KEY, EmailSendSourceEnum.NORMAL.getSendSource(), emailAddress);
    }
    @Autowired
    private PropertiesTools propertiesTools;
    /**
     * 邮箱验证码发送
     * <p>
     * 1、缓存中查找，是否有对应邮箱验证码
     * <p>
     * 2、存在，则不发送邮件：提示 验证码已发送到邮件；
     * <p>
     * 3、不存在，则发送邮件：提示 证码发送邮件成功，并缓存验证码到邮箱
     *
     * @return
     */
    @ApiOperation(value = "邮箱验证码发送", notes = "邮箱验证码发送")
    @PostMapping("/sendMailCaptcha")
    public R<String> sendMailCaptcha(@ApiParam @Valid @RequestBody SendMailCaptchaReq req,
                                            @RequestHeader(required = false, name = "brand") String brand) {
        // 用户有登录信息，则不用走下面流程
        // 根据邮箱
        String emailAddress = req.getEmailAddress();
        if (emailIgnoreWhiteProperties.getWhites().contains(emailAddress)) {
            // 邮箱在白名单中，则直接返回随机验证码，不进行校验
            return R.ok(RandomUtil.randomNumbers(4));
        }
        String realKey = MessageFormat.format(SEND_MAIL_CAPTCHA_KEY, emailAddress);
        // 判定二种发送源是否都有推送验证码
        if (Boolean.TRUE.equals(redisService.hasKey(buildNormalSendSourceKey(emailAddress))) &&
                EmailSendSourceEnum.NORMAL.getSendSource().equals(EmailSendSourceEnum.getSendSourceByCode(req.getSendSource()))) {
            return R.fail(propertiesTools.getProperties(EMAIL_VERIFICATION_ERROR_MSG) );
        }
        if (Boolean.TRUE.equals(redisService.hasKey(buildGmailSendSourceKey(emailAddress)))
                && EmailSendSourceEnum.GMAIL.getSendSource().equals(EmailSendSourceEnum.getSendSourceByCode(req.getSendSource()))) {
            return R.fail(propertiesTools.getProperties(EMAIL_VERIFICATION_ERROR_MSG) );
        }
        // 发送邮件及缓存邮箱对象
        String subject = buildSubject(brand);
        String mailCaptcha = RandomUtil.randomNumbers(4);
        Long minutes = 30L;
        EmailService emailService = emailServiceMap.get(EmailSendSourceEnum.getSendSourceByCode(req.getSendSource()) + EmailConstant.SUFFIX);
        emailService.sendHtmlMail(emailAddress, subject, buildContent(brand, mailCaptcha, minutes));
        // 缓存编码设置 (验证码进行覆盖生成)
        redisService.setCacheObject(realKey, mailCaptcha, minutes, TimeUnit.MINUTES);
        // 记录2中场景
        redisService.setCacheObject(buildCurrentSendSourceKey(req), mailCaptcha, minutes, TimeUnit.MINUTES);
        return R.ok(propertiesTools.getProperties(SEND_MAIL_CAPTCHA_SUCCESS_MSG));
    }

    private String buildSubject(String brand) {
        if (org.apache.commons.lang3.StringUtils.isBlank(brand)) {
            brand = "HiSMK";
        }
        return MessageFormat.format(emailSubject, brand);
    }

    private String buildContent(String brand, String mailCaptcha, Long minutes) {
        return MessageFormat.format(emailContent, brand, mailCaptcha, minutes);
    }

    /**
     * H5  base64图片上传
     */
    @Log(title = "H5图片上传", businessType = BusinessType.UPDATE)
    @PostMapping("/base64/upload")
    @ApiOperation("H5-base64图片上传")
    public R<String> uploadBase64(@RequestParam(value = "file",required = false) MultipartFile file , @RequestParam(value = "base64") String base64) {
        if (StringUtils.isBlank(base64) ) {
            return R.fail("Base64 string is empty");
        }
        R<SysFile> fileResult = remoteFileService.upload(convert(base64,"tmpPic"));
        if (StringUtils.isNull(fileResult) || StringUtils.isNull(fileResult.getData())) {
            return R.fail("File service exception, please contact the administrator");
        }
        return R.ok(fileResult.getData().getUrl(),"success.");
    }

    private static MultipartFile convert(String base64String, String fileName) {
        // 解析base64字符串，分割出编码格式和数据
        String[] base64Info = base64String.split(";base64,");
        String base64Content = base64Info[1]; // 数据部分
        String contentType = base64Info[0].split(":")[1].split(";")[0]; // 文件类型
        // 解码Base64字符串为字节数组
        byte[] bytes = Base64.getDecoder().decode(base64Content);
        // 创建MockMultipartFile对象并设置文件内容和文件名
        return new MockMultipartFile(fileName, fileName +"." + contentType.split("/")[1], contentType, bytes);
    }

    /**
     * H5图片上传
     */
    @Log(title = "H5图片上传", businessType = BusinessType.UPDATE)
    @PostMapping("/upload")
    @ApiOperation("H5图片上传")
    public HiResult<String> upload(@RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            String extension = FileTypeUtils.getExtension(file);
            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
                return HiResultUtil.error("The file format is incorrect, please upload" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + " file format");
            }
            R<SysFile> fileResult = remoteFileService.upload(file);
            if (StringUtils.isNull(fileResult)) {
                return HiResultUtil.error("File service exception, please contact the administrator");
            }
            if (StringUtils.isNull(fileResult.getData())) {
                return HiResultUtil.error("File service exception, please contact the administrator" + fileResult.getMsg());
            }
            String url = fileResult.getData().getUrl();
            return HiResultUtil.success("success.", url);
        }
        return HiResultUtil.error("Upload image exception, please contact the administrator");
    }

    @PostMapping("/uploadNoLogin")
    @ApiOperation("H5图片上传-无登录上传")
    public HiResult<String> uploadNoLogin(@RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            String extension = FileTypeUtils.getExtension(file);
            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
                return HiResultUtil.error("The file format is incorrect, please upload" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + " file format");
            }
            R<SysFile> fileResult = remoteFileService.upload(file);
            if (StringUtils.isNull(fileResult)) {
                return HiResultUtil.error("File service exception, please contact the administrator");
            }
            if (StringUtils.isNull(fileResult.getData())) {
                return HiResultUtil.error("File service exception, please contact the administrator" + fileResult.getMsg());
            }
            String url = fileResult.getData().getUrl();
            return HiResultUtil.success("success.", url);
        }
        return HiResultUtil.error("Upload image exception, please contact the administrator");
    }

    /**
     * H5视频上传
     */
    @Log(title = "H5视频上传", businessType = BusinessType.UPDATE)
    @PostMapping("/video/upload")
    @ApiOperation("H5视频上传")
    public R<SysFile> uploadVideo(@RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            String extension = FileTypeUtils.getExtension(file);
            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.VIDEO_EXTENSION)) {
                return R.fail("The file format is incorrect, please upload" + Arrays.toString(MimeTypeUtils.VIDEO_EXTENSION) + " file format");
            }
            R<SysFile> fileResult = remoteFileService.uploadVideo(file);
            if (StringUtils.isNull(fileResult)) {
                return R.fail("File service exception, please contact the administrator");
            }
            if (StringUtils.isNull(fileResult.getData())) {
                return R.fail("File service exception, please contact the administrator" + fileResult.getMsg());
            }
            SysFile url = fileResult.getData();
            return R.ok(url);
        }
        return R.fail("Upload video exception, please contact the administrator");
    }

    /**
     * H5音视频合并上传
     */
    @Log(title = "H5音视频合并上传", businessType = BusinessType.UPDATE)
    @PostMapping("/video/audioMerge")
    @ApiOperation("H5音视频合并上传")
    public R<SysFile> mergeAudioAndVideo(@RequestParam("file") MultipartFile file,
                                         @RequestParam(value = "audioPath", required = false) String audioPath,
                                         @ApiParam(value = "音频开始时间，单位s")
                                         @RequestParam(value = "audioStartTime", defaultValue = "0", required = false) String audioStartTime,
                                         @ApiParam(value = "音频截取时长，单位s")
                                         @RequestParam(value = "audioDuration", defaultValue = "0", required = false) String audioDuration,
                                         @RequestParam(value = "videoCoverUrl", required = false) String videoCoverUrl) {
        if (!file.isEmpty()) {
            String extension = FileTypeUtils.getExtension(file);
            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.VIDEO_EXTENSION)) {
                log.error("The file format is incorrect, please upload" + Arrays.toString(MimeTypeUtils.VIDEO_EXTENSION) + " file format");
                return R.fail("The file format is incorrect, please upload" + Arrays.toString(MimeTypeUtils.VIDEO_EXTENSION) + " file format");
            }
            R<SysFile> fileResult = remoteFileService.mergeAudioAndVideo(file,audioPath, audioStartTime, audioDuration, videoCoverUrl);
            if (StringUtils.isNull(fileResult)) {
                log.error("File service exception, please contact the administrator");
                return R.fail("File service exception, please contact the administrator");
            }
            if (StringUtils.isNull(fileResult.getData())) {
                log.error("File service exception, please contact the administrator:" + fileResult.getMsg());
                return R.fail("File service exception, please contact the administrator:" + fileResult.getMsg());
            }
            SysFile url = fileResult.getData();
            log.error("File merge ok:" + url.toString());
            return R.ok(url);
        }
        return R.fail("Merge video exception, please contact the administrator");
    }

    /**
     * H5音频上传
     */
    @Log(title = "H5音频上传", businessType = BusinessType.UPDATE)
    @PostMapping("/audio/upload")
    @ApiOperation("H5音频上传")
    public R<SysFile> uploadAudio(@RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            String extension = FileTypeUtils.getExtension(file);
            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.AUDIO_EXTENSION)) {
                return R.fail("The file format is incorrect, please upload" + Arrays.toString(MimeTypeUtils.AUDIO_EXTENSION) + " file format");
            }
            R<SysFile> fileResult = remoteFileService.uploadAudio(file);
            if (StringUtils.isNull(fileResult)) {
                return R.fail("File service exception, please contact the administrator");
            }
            if (StringUtils.isNull(fileResult.getData())) {
                return R.fail("File service exception, please contact the administrator" + fileResult.getMsg());
            }
            SysFile url = fileResult.getData();
            return R.ok(url);
        }
        return R.fail("Upload audio exception, please contact the administrator");
    }
}
